﻿// Imports
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;

namespace Nickel
{

    /// <summary>
    /// The blog
    /// </summary>
    public class Blog
    {

        /// <summary>
        /// List of articles
        /// </summary>
        public List<Article> Articles;

        /// <summary>
        /// List of authors
        /// </summary>
        public List<Author> Authors;

        /// <summary>
        /// Path to backup directory
        /// </summary>
        private string backupPath;

        /// <summary>
        /// List of tags
        /// </summary>
        public Words Tags;

        /// <summary>
        /// Path to JSON file
        /// </summary>
        private string filePath;

        /// <summary>
        /// Creates a blog instance
        /// </summary>
        public Blog()
        {

            // Initialize lists
            Articles = new List<Article>();
            Authors = new List<Author>();
            Tags = new Words();

        }

        /// <summary>
        /// Return articles from author
        /// </summary>
        /// <param name="authorId">Author GUID</param>
        /// <returns>List of articles</returns>
        public List<Article> GetArticlesByAuthorId(string authorId)
        {

            // Initialize return list
            List<Article> articles = new List<Article>();

            // Find articles
            int articleCount = Articles.Count;
            for (int i = 0; i < articleCount; i++) 
                if (Articles[i].Author == authorId) 
                    articles.Add(Articles[i]);

            // Null if none found
            if (articles.Count < 1) 
                articles = null;

            // Return list
            return articles;

        }

        /// <summary>
        /// Return articles from author
        /// </summary>
        /// <param name="authorName">Author name</param>
        /// <returns>List of articles</returns>
        public List<Article> GetArticlesByAuthorName(string authorName)
        {

            // Use author name to get ID and find articles
            Author author = GetAuthorByName(authorName);
            return author == null ? null : GetArticlesByAuthorId(author.ID);

        }

        /// <summary>
        /// Return article using ID
        /// </summary>
        /// <param name="id">Article GUID</param>
        /// <returns>The article</returns>
        public Article GetArticleById(string id)
        {

            // Check ID
            if (StringHelper.ValidGuid.IsMatch(id))
            {

                // Find article
                int articleCount = Articles.Count;
                for (int i = 0; i < articleCount; i++) 
                    if (Articles[i].ID == id)
                        return Articles[i];

            }

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Return article using title
        /// </summary>
        /// <param name="title">Article title</param>
        /// <returns>The article</returns>
        public Article GetArticleByTitle(string title)
        {

            // Find article
            title = StringHelper.Encode(title);
            int articleCount = Articles.Count;
            for (int i = 0; i < articleCount; i++) 
                if (StringHelper.Encode(Articles[i].Title) == title) 
                    return Articles[i];

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Return articles with tag
        /// </summary>
        /// <param name="tag">Tag to find</param>
        /// <returns>List of articles</returns>
        public List<Article> GetArticlesByTag(string tag)
        {

            // Initialize return list
            List<Article> articles = new List<Article>();

            // Find articles
            int articleCount = Articles.Count;
            for (int i = 0; i < articleCount; i++) 
                if (Articles[i].HasTag(tag)) 
                    articles.Add(Articles[i]);

            // Null if none found
            if (articles.Count < 1) 
                articles = null;

            // Return list
            return articles;

        }

        /// <summary>
        /// Finds author with this activation key
        /// </summary>
        /// <param name="key">Author activation key</param>
        /// <returns>The author</returns>
        public Author GetAuthorByActivationKey(string key)
        {

            // Find author
            int authorCount = Authors.Count;
            for (int i = 0; i < authorCount; i++) 
                if (Authors[i].ActivationKey == key) 
                    return Authors[i];

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Finds author with this e-mail address
        /// </summary>
        /// <param name="email">Author e-mail</param>
        /// <returns>The author</returns>
        public Author GetAuthorByEmail(string email)
        {

            // Find author
            email = email.ToLower();
            int authorCount = Authors.Count;
            for (int i = 0; i < authorCount; i++) 
                if (Authors[i].Email.ToLower() == email) 
                    return Authors[i];

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Finds author with this ID
        /// </summary>
        /// <param name="id">Author GUID</param>
        /// <returns>The author</returns>
        public Author GetAuthorById(string id)
        {

            // Check ID
            if (StringHelper.ValidGuid.IsMatch(id))
            {

                // Find author
                int authorCount = Authors.Count;
                for (int i = 0; i < authorCount; i++) 
                    if (Authors[i].ID == id) 
                        return Authors[i];

            }

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Finds author with this name
        /// </summary>
        /// <param name="name">Author name</param>
        /// <returns>The author</returns>
        public Author GetAuthorByName(string name)
        {

            // Find author
            name = StringHelper.Encode(name);
            int authorCount = Authors.Count;
            for (int i = 0; i < authorCount; i++) 
                if (StringHelper.Encode(Authors[i].Name) == name) 
                    return Authors[i];

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Finds author using sign in info
        /// </summary>
        /// <param name="email">Author e-mail</param>
        /// <param name="password">Author password</param>
        /// <returns>The author</returns>
        public Author GetAuthorBySignIn(string email, string password)
        {

            // Find author
            email = email.ToLower();
            int authorCount = Authors.Count;
            for (int i = 0; i < authorCount; i++) 
                if (Authors[i].Email.ToLower() == email && Authors[i].Password == password) 
                    return Authors[i];

            // Return null if not found
            return null;

        }

        /// <summary>
        /// Removes tag from articles
        /// </summary>
        /// <param name="tag">Tag to remove</param>
        /// <returns>Number of articles affected</returns>
        public int RemoveTagFromArticles(string tag)
        {

            // Start count
            int tagsRemoved = 0;

            // Remove tags
            int articleCount = Articles.Count;
            for (int i = 0; i < articleCount; i++) 
                if (Articles[i].Tags.Remove(tag)) 
                    tagsRemoved++;

            // Return count
            return tagsRemoved;

        }

        /// <summary>
        /// Check if title exists in articles or tags
        /// </summary>
        /// <param name="title">The title</param>
        /// <returns>True if taken</returns>
        public string TitleExists(string title, string strPath)
        {

            // Check title without ID
            return TitleExists(title, string.Empty, strPath);

        }

        /// <summary>
        /// Check if title exists in articles or tags
        /// </summary>
        /// <param name="title">The title</param>
        /// <returns>True if taken</returns>
        public string TitleExists(string title, string strId, string strPath)
        {

            // Encode title
            title = StringHelper.Encode(title);

            // Titles can't be numbers
            int intTitle;
            if (int.TryParse(title, out intTitle)) 
                return typeof(int).ToString();

            // Look for encoded title in articles
            int articleCount = Articles.Count;
            for (int i = 0; i < articleCount; i++)
            {

                // No ID specified
                if (strId == string.Empty)
                {

                    // Compare titles
                    if (StringHelper.Encode(Articles[i].Title) == title) 
                        return typeof(Article).ToString();

                }
                else
                {

                    // Compare titles, disregarding article if ID matches
                    if (StringHelper.Encode(Articles[i].Title) == title && strId != Articles[i].ID) 
                        return typeof(Article).ToString();

                }

            }

            // Look for encoded name in authors
            int authorCount = Authors.Count;
            for (int i = 0; i < authorCount; i++)
            {

                // No ID specified
                if (strId == string.Empty)
                {

                    // Compare titles
                    if (StringHelper.Encode(Authors[i].Name) == title) 
                        return typeof(Author).ToString();

                }
                else
                {

                    // Compare titles, disregarding author if ID matches
                    if (StringHelper.Encode(Authors[i].Name) == title && strId != Authors[i].ID) 
                        return typeof(Author).ToString();

                }

            }

            // Look for encoded title in tags
            int tagCount = Tags.Count;
            for (int i = 0; i < tagCount; i++) 
                if (StringHelper.Encode(Tags[i]) == title) 
                    return typeof(string).ToString();

            // Check file and directory names
            foreach (string directory in Directory.GetDirectories(strPath)) 
                if (StringHelper.Encode(directory.Substring(directory.LastIndexOf("\\") + 1)) == title) 
                    return typeof(string).ToString();
            foreach (string file in Directory.GetFiles(strPath)) 
                if (StringHelper.Encode(file.ToLower().Substring(file.LastIndexOf("\\") + 1)) == title) 
                    return typeof(string).ToString();

            // Return false if no match
            return string.Empty;

        }

        /// <summary>
        /// Load JSON file and parse to Blog object
        /// </summary>
        /// <param name="strFilePath">Absolute path to file</param>
        public void Load(string strFilePath)
        {

            // Set default file path
            filePath = strFilePath;

            // Set path to backup directory
            backupPath = filePath.Substring(0, filePath.LastIndexOf("\\")) + "\\backup\\";

            // Initialize reader
            StreamReader reader = null;

            // Open file (this block retries until file is open 
            // in case file is in use)
            while (reader == null)
            {
                try
                {
                    reader = new StreamReader(strFilePath);
                }
                catch
                {
                }
            }

            // Open JSON file
            reader = new StreamReader(strFilePath);
            LoadJSON(reader.ReadToEnd());

            // Close file
            if (reader != null) reader.Close();
            reader = null;

        }

        /// <summary>
        /// Load blog from JSON string
        /// </summary>
        /// <param name="strJSON"></param>
        public void LoadJSON(string strJSON)
        {

            // Parse JSON
            Blog data = JsonConvert.DeserializeObject<Blog>(strJSON);

            // Populate lists
            Articles = data.Articles;
            Authors = data.Authors;
            Tags = data.Tags;

            // Sort lists
            Articles.Sort(Sort.ArticleByDate);
            Authors.Sort(Sort.AuthorByName);
            Tags.Sort();

            // Clean up
            data = null;

        }

        /// <summary>
        /// Save Blog object to JSON file at default path
        /// </summary>
        public void Save()
        {

            // Save
            Save(filePath);

        }

        /// <summary>
        /// Save Blog object to JSON file at specified path
        /// </summary>
        /// <param name="strFilePath">Absolute path to file</param>
        public void Save(string strFilePath)
        {

            // Make backup copy
            File.Copy(strFilePath, backupPath + "Data_" + DateTime.Now.ToString("yyyy-MM-dd_hh-mm-ss-tt") + ".json");

            // Initialize writer
            StreamWriter writer = null;

            // Open file (this block retries until file is open 
            // in case file is in use)
            while (writer == null)
            {
                try
                {
                    writer = new StreamWriter(strFilePath);
                }
                catch
                {
                }
            }

            // Convert blog to JSON and save
            writer.Write(JsonConvert.SerializeObject(this));

            // Close the file
            if (writer != null) 
                writer.Close();
            writer = null;

        }

        /// <summary>
        /// Clean up
        /// </summary>
        public void Dispose()
        {

            // Clear lists
            Authors.Clear();
            Authors = null;
            Articles.Clear();
            Articles = null;
            Tags.Clear();
            Tags = null;

        }

    }

}
